from utils.idandmeta import IdAndMeta


class Action(IdAndMeta):
    """Action: represents a movement of items using the stacking crane. Actions can be of two 
    types, pickup or putdown, possibly with several items."""
    PICKUP  = 0
    PUTDOWN = 1
    
    def __init__(self, id, type, items, place, internal, **meta):
        """
        type <int> - action type (enum). Either Action.PICKUP or Action.PUTDOWN.
        items  <[id]> - list of item identifiers.
        place <id> - place identifier.
        internal <bool> - whether the action occurs inside the warehouse or at a boundary place 
            (entrance or exit). If the action occurs at a boundary place, it corresponds to a 
            release or delivery.
        """
        assert type in (Action.PICKUP, Action.PUTDOWN)
        IdAndMeta.__init__(self, id, **meta)
        self.type     = type
        self.items    = list(items)
        self.place    = place
        self.internal = bool(internal)
        
    def __repr__(self):
        if self.is_release:
            action = "released"
        elif self.is_delivery:
            action = "delivered"
        else:
            action = "picked up" if self.is_pickup else "put down"
        items  = ", ".join(str(item_id) for item_id in self.items)
        return ("%s-%s: [%s] %s @ %s %s" % 
                (type(self).__name__, self.id, items, 
                 action, self.place, self.meta))
        
    def __eq__(self, action):
        return (self.type     == action.type  and 
                self.items    == action.items and
                self.place    == action.place and
                self.internal == action.internal)
                
    # ----------------------------------------------------------------------------------------------
    # Useful properties
    @property
    def is_pickup(self):
        return self.type == Action.PICKUP
        
    @property
    def is_putdown(self):
        return self.type == Action.PUTDOWN
        
    @property
    def is_release(self):
        return not self.internal and self.type == Action.PICKUP
        
    @property
    def is_delivery(self):
        return not self.internal and self.type == Action.PUTDOWN
        
    # ----------------------------------------------------------------------------------------------
    # Additional methods
    def deepcopy(self):
        clone = type(self)(self.id, self.type, self.items, self.place, self.internal)
        clone.meta = self.meta.deepcopy()
        return clone
        
    def reverse(self):
        """Reverses an action in-place."""
        self.type = Action.PICKUP if self.is_putdown else Action.PUTDOWN
        self.items.reverse()
        
    def reversed(self):
        """Returns a reversed copy of the action."""
        action = self.deepcopy()
        action.reverse()
        return action
        
    # ----------------------------------------------------------------------------------------------
    # Shortcut constructors
    @classmethod
    def pickup(cls, id, items, place, **meta):
        return cls(id, Action.PICKUP, items, place, internal=True, **meta)
        
    @classmethod
    def putdown(cls, id, items, place, **meta):
        return cls(id, Action.PUTDOWN, items, place, internal=True, **meta)
        
    @classmethod
    def rpickup(cls, id, items, place, **meta):
        return cls(id, Action.PICKUP, items, place, internal=False, **meta)
        
    @classmethod
    def dputdown(cls, id, items, place, **meta):
        return cls(id, Action.PUTDOWN, items, place, internal=False, **meta)
        
        
